#include "MainWindow.h"
#include <QFileDialog>
#include <stdexcept>
#include <QDesktopWidget>
#include <cstdlib>
#include <ctime>
#include <QPolygonF>
#include <iostream>

namespace qtangram
{

MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent), patternManager(this,-1,1), player(0), scores(0), winDialog(0), winState(false), tol(0.2), hasPlayerName(false) {
    srand(time(NULL));
    setupUi(this);
    move( (QApplication::desktop()->width() - width()) / 2, (QApplication::desktop()->height() - height()) / 2);
    music = Phonon::createPlayer(Phonon::MusicCategory, Phonon::MediaSource(":/troll.mp3"));
    player = new Player(this, Qt::WindowStaysOnTopHint);
    scores = new Scores(this);
    trollface = new Trollface(this);
    winDialog = new WinDialog(this);
    setupConnections();
    init();
    player->show();
}

MainWindow::~MainWindow() {}
void MainWindow::setupConnections() {
    connect(actionSave, SIGNAL(triggered()), this, SLOT(onSave()));
    connect(actionClose, SIGNAL(triggered()), this, SLOT(onClose()));
    connect(actionChooseTangram, SIGNAL(triggered()), this, SLOT(onOpen()));
    connect(actionCreationMode, SIGNAL(triggered()), this, SLOT(onCreationModeEntered()));
    connect(actionPause, SIGNAL(triggered()), this, SLOT(onPause()));
    connect(actionScores, SIGNAL(triggered()), this, SLOT(onScores()));
    connect(actionAbout, SIGNAL(triggered()), this, SLOT(playMusic()));
    connect(actionAbout, SIGNAL(triggered()), this, SLOT(onAbout()));
    connect(actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
    connect(workspaceWindow, SIGNAL(sigRemoveSelectedTans()), this, SLOT(onMoveTanToTray()));
    connect(trayWindow, SIGNAL(sigRemoveSelectedTans()), this, SLOT(onMoveTanToWorkspace()));
    connect(&patternManager, SIGNAL(patternLoaded(QLinkedList<Tan>)), patternWindow, SLOT(setPatternPolygon(QLinkedList<Tan>)));
    connect(&patternManager, SIGNAL(patternLoaded(QLinkedList<Tan>)), workspaceWindow, SLOT(setPatternPolygon(QLinkedList<Tan>)));
    connect(&patternManager, SIGNAL(patternLoaded(QLinkedList<Tan>)), this, SLOT(onPatternLoaded(QLinkedList<Tan>)));
    connect(workspaceWindow, SIGNAL(sigCheckIfWon()), this, SLOT(onCheckIfWon()));
    connect(actionChangeName, SIGNAL(triggered()), this, SLOT(onChangeName()));
    connect(player, SIGNAL(playerNameEntered(QString)), this, SLOT(onPlayerNameEntered(QString)));
    connect(this, SIGNAL(sigPositiveMatch()), this, SLOT(onPositiveMatch()));
    connect(tolerance, SIGNAL(valueChanged(double)), this, SLOT(onToleranceChange(double)));
    connect(winDialog, SIGNAL(sigOkClicked()), this, SLOT(onWinDlgOkClicked()));
    connect(winDialog, SIGNAL(sigAgainClicked()), this, SLOT(onWinDlgAgainClicked()));
    #ifdef __DEBUG__
    connect(tolerance, SIGNAL(valueChanged(double)), workspaceWindow, SLOT(onToleranceChange(double)));
    #endif
}
void MainWindow::onPlayerNameEntered(QString) {
    hasPlayerName = true;
}

void MainWindow::init() {
    trayWindow->tans.clear();
    trayWindow->selection.clear();
    workspaceWindow->tans.clear();
    workspaceWindow->selection.clear();
    tangram.tans.clear();
    tangram.loadTangramFromFile("../data/Tans/original.set");
    for(auto it = tangram.tans.begin(); it != tangram.tans.end(); ++it)
	    trayWindow->tans.push_back(&(*it));
    repaint();
}

void MainWindow::onWinDlgOkClicked() {
    onCreationModeEntered(); //acts like in creation mode
    winState = false;
}

void MainWindow::onWinDlgAgainClicked() {
    init();
    winState = false;
    clock->start();
}

void MainWindow::onCreationModeEntered() {
    clock->reset();
    init();
    patternWindow->clearPattern();
    patternWindow->repaint();
    workspaceWindow->clearPattern();
    workspaceWindow->repaint();
    repaint();
}

void MainWindow::onToleranceChange(double value) {
    tol = value;
    try {
	pattern = summEnlargedPolygons(patternOriginal,tol);
	onCheckIfWon();
    }
    catch(const std::runtime_error& e) { std::cerr << "Sum returned exception: " << e.what(); }
}

void MainWindow::onPatternLoaded(QLinkedList<Tan> orig) {
    patternOriginal = orig;
    try {
	pattern = summEnlargedPolygons(patternOriginal,tol);
	clock->start();
    }
    catch(const std::runtime_error& e) { std::cerr << "Sum returned exception: " << e.what(); }
}

void MainWindow::onChangeName() {
    player->show();
}

void MainWindow::onOpen() {
    patternManager.load();
}

void MainWindow::onSave() {
    try {
        if(!trayWindow->tans.isEmpty())
            throw std::runtime_error("To create tangram you must use all available tans.");
        patternManager.save(tangram.tans);
    }
    catch(const std::exception& e) {
        QMessageBox::critical(this,"Export failure",e.what());
    }
}

void MainWindow::onClose() {
    QCoreApplication::exit(0);
}

void MainWindow::onPause() {
    clock->pause();
    trollface->show();
}

void MainWindow::onScores() {
    scores->display();
}

void MainWindow::onAbout() {
    qsrand(666);
    QPixmap troll(":/trollsmall.png");
    for(int i=0; i<100; ++i) {
        QMessageBox * msg = new QMessageBox(this);
        msg->setIconPixmap(troll);
        msg->setWindowTitle("trololo");
        msg->move((double)(qrand()/(double)RAND_MAX)*(double)QApplication::desktop()->width(), (double)(qrand()/(double)RAND_MAX)*(double)QApplication::desktop()->height());
        msg->show();
    }
    QMessageBox::information(this,"Information",QString::fromUtf8("<h2>Tangram v. 1.0</h2> "
			     "<p><b>Authors (in alphabetical order): Przemysław Plutecki, Maciej Siczek, Paweł Zadrożniak</b></p>"));
}

void MainWindow::playMusic() {
    music->play();
}

void MainWindow::stopMusic() {
    music->stop();
}

void MainWindow::onMoveTanToTray() {
    auto it_selection = workspaceWindow->selection.begin();
    while (it_selection != workspaceWindow->selection.end()) {
        workspaceWindow->tans.removeOne(*it_selection);
        trayWindow->tans.append(*it_selection);
        (*it_selection)->selected = false;
        (*it_selection)->setDefaultPosition(); // thx Silent!
        it_selection = workspaceWindow->selection.erase(it_selection);
    }
    trayWindow->repaint();
    workspaceWindow->repaint();
}

void MainWindow::onMoveTanToWorkspace() {
    QRectF wrld(QPointF(-workspaceWindow->getViewRange()/2.0,workspaceWindow->getViewRange()/2.0),
                QPointF(workspaceWindow->getViewRange()/2.0,-workspaceWindow->getViewRange()/2.0)   );
    int attempts;
    bool valid;
    // draft mode on
    auto it_selection = trayWindow->selection.begin();
    while (it_selection != trayWindow->selection.end()) {
        trayWindow->tans.removeOne(*it_selection);
        workspaceWindow->tans.append(*it_selection);
        (*it_selection)->selected = false;
        attempts=100;
        do
        {
            valid=true;
            for(auto it=workspaceWindow->tans.begin(); it!=workspaceWindow->tans.end(); ++it)
            {
                if ( (*it_selection) != (*it) )
                {
                    if ((*it_selection)->polyCurrent.intersected((*it)->polyCurrent).count()) valid=false;
                }
            }
            for (auto it=(*it_selection)->polyCurrent.begin(); it!=(*it_selection)->polyCurrent.end(); ++it)
            {
                if (!wrld.contains(*it)) valid=false;
            }
            if (!valid)
	    {
                (*it_selection)->currentPoint.setX(((double)(rand()/(double)RAND_MAX) * workspaceWindow->getViewRange()) - (workspaceWindow->getViewRange()/2.0 ));
                (*it_selection)->currentPoint.setY(((double)(rand()/(double)RAND_MAX) * workspaceWindow->getViewRange()) - (workspaceWindow->getViewRange()/2.0 ));
                (*it_selection)->recalculateCurrentPolygon();
            }
            --attempts;
        } while(attempts && !valid);

        it_selection = trayWindow->selection.erase(it_selection);
    }
    trayWindow->repaint();
    workspaceWindow->repaint();
    // draft mode off
}

bool MainWindow::onCheckIfWon() {
    try {
	if(workspaceWindow->tans.size() != tangram.tans.size()) return false;
	for(auto it = tangram.tans.begin(); it != tangram.tans.end(); ++it) {
	    for(auto it2 = (*it).polyCurrent.begin(); it2 != (*it).polyCurrent.end(); ++it2) {
		if(!(pattern.containsPoint(*it2,Qt::OddEvenFill))) {
		    return false;
		}
	    }
	}
	emit sigPositiveMatch();
	return true;
    }
    catch(const std::runtime_error&) { return false; }
}

void MainWindow::onPositiveMatch() {
    clock->stop();
    if(!winState) {
	scores->add(ScoresItem(clock->getSeconds(),clock->getTime(),player->getName()));
	winDialog->setText("Congratulations, you won!\nYour time: " + clock->getTime());
	winDialog->show();
    }
    winState = true;
}

} // namespace qtangram
